13. Projekt indywidualny - tydzień 2 z 3
Wyzwania:
- nauczysz się testować swoją stronę na wielu rozdzielczościach
- dowiesz się, w jaki sposób na wąskich ekranach wyświetlać szerokie tabele i wykresy
- przygotujesz obrazki dla różnych typów ekranu
- przypomnisz sobie informacje dot. formularzy
- lepiej poznasz rodzaje kontrolek oraz ich atrybuty
- dowiesz się jak zaimplementować walidację formularza
Wstęp
W zeszłym tygodniu omówiliśmy sobie jak rozpocząć projekt oraz na czym polega podejście komponentowe. W tym tygodniu przygotowaliśmy uzupełnienie dla modułu związanego z Responsive Web Design. Pamiętaj jednak, żeby zajrzeć do tamtego modułu — znajdziesz w nim wiele kluczowych informacji, taki jak np. działanie viewport, który jest niezbędny do poprawnego funkcjonowania responsywności strony.
Przyszedł też czas na poruszenie tematu, bez którego aplikacje internetowe nie miałyby sensu – formularzy. I znowu temat może wydawać się dość prosty, bo nie jest problemem zrobienie kilku pól. Jednak kiedy chcemy zrobić coś bardziej zaawansowanego, to niezbędna okazuje się wiedza na temat działania kontrolek formularzy. Dlatego omówimy sobie większość kontrolek formularzy, ich właściwości, oraz jak je stylować. Dodatkowo zobaczysz, na czym polega walidacja formularzy, i w jaki sposób możemy ją przeprowadzić.
13.1. Testowanie RWD
Jak dobrze wiesz z poprzednich modułów, w internecie mamy coraz więcej ruchu mobilnego, co oznacza, że coraz więcej użytkowników do przeglądania stron internetowych wykorzystuje różnego rodzaju urządzenia typu smartfon czy tablet. Dlatego musimy sprawić, aby strona dobrze się wyświetlała przynajmniej na najpopularniejszych rozdzielczościach. Jednak nie warto przy tym pomijać użytkowników ze specyficznymi wielkościami ekranów.
Kiedy zaczęto tworzyć responsywne strony internetowe, to testowanie polegało na sprawdzeniu witryny na tylu urządzeniach, ile się posiadało. Dzisiaj jest to o wiele łatwiejsze i przyjemniejsze, a większość problemów jesteśmy w stanie rozwiązać w przeglądarce na komputerze.
Developers Tools w przeglądarkach
Wszystkie popularne przeglądarki mają swoje narzędzia dla developerów stron internetowych. Dzięki nim możemy zobaczyć, jakie style działają na konkretny element, oraz zbadać jego właściwości.
Wystarczy, że klikniesz prawym przyciskiem myszy na dowolnym elemencie na stronie i z menu kontekstowego wybierzesz opcję "Zbadaj element" (ang. Inspect element). Otworzy Ci się okienko narzędzi developerskich.
Więcej o samych narzędziach możesz dowiedzieć się z archiwalnego webinara Kodilli.
Tryb responsywny
Wszystkie przeglądarkowe narzędzia developerskie dysponują tzw. "trybem responsywnym", który służy do testowania naszej strony na różnych rozdzielczościach ekranu.
Aby przejść do tego trybu w Google Chrome, Safari czy Operze, wystarczy kliknąć w przycisk telefonu znajdujący się w lewym górnym rogu panelu narzędzi developerskich.
Natomiast w Firefoksie znajdziemy go w prawym górnym rogu.
Kiedy go włączymy, zobaczymy, że nasza strona wygląda zupełnie inaczej. Omówmy sobie elementy tego widoku.
1. Suwaki – wystarczy je przeciągnąć, aby zmienić rozdzielczość symulowanego urządzenia.
2. Popularne szerokości – kliknij w odpowiedni panel, a strona dostosuje się do jednej z najpopularniejszych szerokości ekranu. Dobrą praktyką jest dostosowanie strony do każdej z nich. Dzięki temu zyskamy pewność, że obsłużymy większość urządzeń.
3. Predefiniowane urządzenia – możemy zdefiniować i wybrać wielkości ekranów różnych urządzeń. Dzięki temu zobaczymy, jak nasza strona może wyświetlać się na iPhone'ach czy innych zdefiniowanych urządzeniach.
4. Wymiary ekranu – poza przeciąganiem i wyklikiwaniem wielkości ekranów, możemy sami podać dowolne jego rozmiary.
5. Powiększanie – możemy ustawić sobie powiększenie naszego widoku. Jest to przydatne, jeśli mamy zbyt dużą rozdzielczość, aby zobaczyć całą stronę lub zbyt małą rozdzielczość, aby dostrzec detale.
6. Obracanie ekranu – klikając w ten przycisk, zamienimy wysokość z szerokością, symulując zmianę orientacji ekranu.
Znając już jego działanie, możesz bez większych przeszkód testować responsywność swoich stron.
13.2. Mobilne menu
Jedną z największych bolączek początkujących webdeveloperów jest przystosowanie nawigacji strony do standardów RWD. Często bywa tak, że nie dostaniemy zaprojektowanego widoku mobilnego, a tym bardziej widoku mobilnego menu. Co w takiej sytuacji można zrobić? Zastosować sprawdzone praktyki z innych stron.
Popularne rozwiązania
Kiedy spojrzymy na większość stron, to okaże się, że duża ich część będzie miała tzw. typowy podział, czyli logo i przycisk menu zwany potocznie "hamburgerem".
Jak powinno wyglądać otwarte menu? Tutaj opcji mamy bardzo dużo, ale do najpopularniejszych należą:
Rozwijanie w dół z przesunięciem – polega na tym, że menu rozszerza nam górny panel, a cała treść zjeżdża w dół, pozostając tuż za menu. Tego rozwiązania nie używamy w połączeniu ze sticky menu, czyli paskiem menu zawsze widocznym w oknie przeglądarki.
Rozwijanie w dół z nakładką – podobnie jak wyżej. Różnica polega na tym, że menu przykrywa częściowo treść – to rozwiązanie jest najczęściej stosowane przy małej liczbie elementów w menu.
Nakładane menu – otwarte menu przykrywa nam cały ekran i wygląda tak, jakbyśmy przeszli do innej podstrony. To menu może zawierać więcej elementów, ponieważ może być przewijane.
Offcanvas – bardzo efektowna opcja, polegająca na wypchnięciu menu od boku, przy czym treść również jest przesuwana. Warto obejrzeć różne przykłady offcanvas menu.
Budowa menu
Budując menu, powinniśmy zastanowić się nad tym, jak bardzo jego układ i wygląd mają pasować do siebie na desktopie oraz urządzeniach mobilnych. Innymi słowy – czy tym samym HTML-em z różnymi stylami będziemy w stanie zbudować menu w dwóch wersjach. W zależności od sytuacji powinniśmy wybrać odpowiednie rozwiązanie:
1. Podobna struktura – jeśli ilość elementów, z których ma składać się nasze menu, jest taka sama na desktopie, jak i na telefonach, to powinniśmy zrobić wyłącznie jeden kod HTML, a tylko i wyłącznie stylami zróżnicować ich wygląd.
2. Różne struktury – jeśli chcemy, aby nasze menu mobilne miało więcej elementów, możemy albo stworzyć rozbudowaną strukturę HTML, na desktopie ukrywać niektóre elementy, a na telefonie pokazywać wszystkie, LUB wydzielić menu do osobnego kontenera i mieć w HTML-u dwa niezależne elementy menu.
Sterowanie menu
Mimo że podstawy JavaScriptu omówiliśmy sobie dopiero niedawno i prawdopodobnie jest to jeszcze dla Ciebie czarna magia, to wbrew pozorom sterowanie menu nie wymaga od Ciebie niczego nowego.
Omówmy sobie założenia działania menu. To, jak ma ono wyglądać na desktopie, tabletach i telefonie, jesteś w stanie zrobić za pomocą CSS. Jedyne, co musisz zrobić, to sterować menu tak, żeby wiedzieć, czy ma być ono widoczne, czy nie. W tym celu warto założyć sobie, że sterowanie będzie polegać na dodawaniu klasy, jeśli menu ma być widoczne, oraz jej usunięciu, jeśli będziemy chcieli je ukrywać. Warto tutaj pamiętać, że ta reguła powinna działać tylko na szerokościach, kiedy mamy mobilne menu, czyli istnienie klasy nie powinno mieć wpływu na wyświetlanie się menu na desktopie. To również jesteśmy w stanie przygotować sobie w CSS.
Kiedy mamy już przygotowane wszystkie style związane z menu, przechodzimy do oskryptowania jego działania.
W pierwszej kolejności napiszmy sobie uniwersalną funkcję, która będzie pokazywać i ukrywać menu:
function toggleMenu(visible) {
document.querySelector('.menu').classList.toggle('show', visible)
}
Po wywołaniu toggleMenu(true), do obiektu z klasą menu zostanie dodana klasa show, a po wywołaniu toggleMenu(false) zostanie ona usunięta. Dodatkowo, jeśli użyjemy tej funkcji bez argumentu toggleMenu(), to w zależności od tego, czy klasa show już istnieje, czy nie, to zostanie dodana lub usunięta.
Później wystarczy ją podczepić do wybranych elementów za pomocą:
document.querySelector('.hamburger').addEventListener('click', function(e) {
e.preventDefault();
toggleMenu()
});
Jak widać, stworzenie sterowania menu może być całkiem łatwe.
13.3. Responsywne tabele i wykresy
Kolejnym z problemów, na który możesz natrafić, jest problem z wyświetlaniem długich treści, których nie można zwinąć lub zmniejszyć, takich jak wykresy czy tabele. Jeśli tabela jest wąska, to wystarczy dodać jej width: 100% lub skorzystać z przestawnych tabel, które mogą wyglądać jak w tym przykładzie.
Jednak jeśli mamy dużo kolumn lub jeśli potrzebujemy pokazać szeroki obrazek, który na telefonie może być nieczytelny, wtedy powinniśmy skorzystać z tzw. scroll wrappera.
Scrollowany wrapper
Scrollowany wrapper to nic innego jak element, który zawiera w sobie większy element i jest możliwy do scrollowania w poziomie. Podobne rozwiązanie można zauważyć również w Bootstrapie. W jaki sposób zrobić takie rozwiązanie?
Zacznijmy od stworzenia tabelki:
<table>
...
</table>
Następnie owińmy ją divem, który będzie naszym scroll wrapperem, i dodajmy mu taką oto klasę:
<div class="scroll-wrapper">
<table>
...
</table>
</div>
A później dodajmy style:
.scroll-wrapper {
display: block;
width: 100%;
overflow-x: auto;
}
.scroll-wrapper table {
width: 100%;
max-width: 100%;
}
I dzięki temu, kiedy będzie trzeba, to tabelka będzie możliwa do scrollowania.
W podobny sposób możemy postąpić z obrazkami wykresów, tyle że trzeba pamiętać, że obrazek musi mieć wtedy width: auto;.
13.4. Responsywny grid
Kolejną rzeczą, którą warto lepiej poznać, jest praca z responsywnym gridem. Na początku kursu powiedzieliśmy Ci, czym jest grid, i w jaki sposób go wykorzystać. Teraz czas na pewną dobrą praktykę związaną z responsywnym gridem, czyli takim, który możemy definiować dla różnych szerokości ekranu.
Zapewne pamiętasz, jak używaliśmy Bootstrapa, gdzie mieliśmy możliwość określić szerokość kolumny osobno dla wielkości small, medium i large, np. col-sm-12 col-md-6 col-lg-3. Dzięki takim klasom kolumna na małych ekranach zajmuje 100% miejsca, na tabletach połowę, a na desktopie jedną czwartą.
Przygotujmy podobny grid, ale tym razem wykorzystamy do tego celu Sassa i spróbujemy go wygenerować dynamicznie.
Zacznijmy od przygotowania sobie założeń dla gridu:
- budowa bazująca na flexboksie,
- podejście boxed i fluid,
- 12 kolumn,
- podejście mobile-first, czyli domyślnie definiujemy dla najmniejszych urządzeń, a tylko większe będziemy rozszerzać,
- 4 zakresy: do 767px, od 768px, od 992px, od 1200px. , Jak pamiętasz w poprzednich modułach używaliśmy gridu opartego o floaty. Pokażemy Ci, że można zbudować grid na różne sposoby. Warto wiedzieć, że Bootstrap w wersji 4 zaczął korzystać z flexboksa, dlatego wspólnie zrobimy podobne rozwiązanie.
Container
Zacznijmy od wyseparowania klas odpowiedzialnych za budowę boxed i fluid. Podejście boxed polega na tym, że główny kontener ma swoją określoną maksymalną szerokość, natomiast fluid ma pełną dostępną szerokość.
* {
box-sizing: border-box;
}
.container {
width: 100%;
max-width: 1170px;
margin: 0 auto;
padding-left: 15px;
padding-right: 15px;
}
.container-fluid {
width: 100%;
padding-left: 15px;
padding-right: 15px;
}
Row
Teraz przejdziemy do stworzenia bazy dla naszego grida, czyli row.
.row {
display: flex;
flex-wrap: wrap;
}
Kolumny
A następnie przygotujemy sobie wzór stylów kolumn.
.col-sm-1 {
flex: 0 0 8.33333%;
max-width: 8.33333%;
}
.col-sm-2 {
flex: 0 0 16.66667%;
max-width: 16.66667%;
}
.col-sm-3 {
flex: 0 0 25%;
max-width: 25%;
}
...
Spróbujmy z tego zrobić pętlę. Jeśli chcesz lepiej zrozumieć działanie pętli, zapoznaj się z dokumentacją.
@for $i from 1 through 12 {
.col-sm-#{$i} {
$width: 100% / 12 * $i;
flex: 0 0 $width;
max-width: $width;
}
}
Następnie stworzymy mixin, który na podstawie prefiksu dla danej szerokości zbuduje nam listę klas:
@mixin grid($prefix) {
@for $i from 1 through 12 {
.col-#{$prefix}-#{$i} {
$width: 100% / 12 * $i;
flex: 0 0 $width;
max-width: $width;
}
}
}
Implementacja grida w media queries
Na końcu dodamy media queries:
@media(max-width: 767px) {
@include grid('sm');
}
@media(min-width: 768px) {
@include grid('md');
}
@media(min-width: 992px) {
@include grid('lg');
}
@media(min-width: 1200px) {
@include grid('xl');
}
W ten sposób, nawet jeśli nie zadeklarujemy, jak ma wyglądać kolumna na desktopie, ale zadeklarujemy, jak ma wyglądać na telefonie, to odziedziczy ona szerokość niższego rzędu. Dzięki temu nasze rozwiązanie spełni warunek mobile-first.
Teraz bez większych przeszkód możesz samodzielnie rozszerzyć grida, lub po prostu z niego skorzystać do zbudowania responsywnego układu.
13.5. Retina i problemy z obrazkami
Ostatnią rzeczą, którą omówimy w tym tygodniu, będzie sprawa obrazków oraz sposobów na ich osadzenie na stronie. Temat może wydawać się dość trywialny, jednak głębsze przypatrzenie się problemom, które są z nim związane, może diametralnie zmienić Twoje podejście do niego.
Gęstość pikseli i Retina
Zacznijmy od wyjaśnienia sobie pewnego pojęcia: gęstość ekranu. Jaki rozmiar ma 1 piksel? Ile to cm? Na to pytanie nie ma jednoznacznej odpowiedzi, ponieważ wszystko zależy od gęstości ekranu. Monitory są różnych wielkości: 20, 24, 32 cale, a telewizory mogą być jeszcze większe, typu 42, 50 czy 60 cali. Jednak w zależności od ich matrycy mogą mieć podobne rozdzielczości ekranu np. FullHD(1920px X 1080px), niezależnie od ich fizycznej wielkości. I wtedy dochodzimy do określenia gęstości ekranu, która mówi o liczbie pikseli bądź punktów na jednostkę miary. Takimi jednostkami gęstości ekranu mogą być DPI (Dots Per Inch) czy PPI (Points Per Ich). Rzadziej trafiamy na DPC (Dots Per Cm) czy inne odwołujące się do innych jednostek miary.
Dlatego duże ekrany mają tę gęstość mniejszą, np. 72 czy 96 DPI, a mniejsze urządzenia typu telefon mają nawet po 300 DPI. A na co wpływa gęstość pikseli? Na jakość wyświetlanych grafik. Im więcej pikseli na małej powierzchni (czyli im większa gęstość), tym wyraźniejszy obraz możemy otrzymać.
Czym zatem jest Retina? Jest to opatentowany przez Apple rodzaj ekranów, gdzie gęstość wynosi zazwyczaj powyżej 330 DPI. Nazwa tej technologii po angielsku oznacza siatkówkę oka — wybrano taką nazwę, ponieważ przy normalnym użytkowaniu ludzkie oko nie jest w stanie rozróżnić poszczególnych pikseli. Jako że nazwa "Retina" jest własnością Apple, szerzej mówimy tu o ekranach HiDPI.
W praktyce wygląda to tak, że jeśli ekran ma rozdzielność 1920px i takiej wielkości będą wyświetlane obiekty, to fizycznie tę szerokość obsługiwać będzie 2 razy więcej pikseli. Dzięki temu obraz jest o wiele bardziej czytelny.
Ceną za lepszą jakość obrazu jest to, że wszystkie elementy statyczne (np. obrazki) powinny mieć 4 razy większe źródła. Stąd mówimy o "Retina Ready" czyli stronach, które wyświetlają się równie dobrze na normalnych urządzeniach oraz na tych z ekranami Retina.
Zwróć uwagę, że w powyższym akapicie napisaliśmy o 4 razy większych źródłach — wynika to z tego, że mają one zarówno 2x większą szerokość, jak i wysokość.
Obrazki a Web Performance
Drugi problem, który może pojawić się z obrazkami na naszej stronie, to ich wpływ na Web Performence, czyli na osiągi strony. Za tym pojęciem kryje się m.in. czas ładowania strony, włączając w to wszystkie jej elementy (w tym obrazki), czy to, jak płynnie strona działa. Można pomyśleć, że na prędkość działania komputera użytkownika nie mamy wpływu. I to jest prawda, ale mamy wpływ na to, żeby strona była lekka, szybko się ładowała i łatwo się po niej nawigowało.
Tutaj istotnym jest przede wszystkim ograniczenie wagi obrazków. Jak sprawdzić, ile nasza strona waży dla użytkownika? Otwórz narzędzia developerskie w przeglądarce i przejdź do zakładki Performence: na dole znajdziesz informację o wadze wszystkich elementów i czasie ładowania strony.
Naszym zadaniem jest zatem osadzanie tylko obrazków w rozmiarach, które faktycznie są używane, oraz ich kompresja. Dlatego, jeśli znajdziesz jakieś fantastyczne zdjęcie, to warto najpierw zobaczyć, jaką ono ma wagę. W różnego rodzaju stockach (bankach zdjęć) znajdziemy przepiękne, wysokiej rozdzielczości zdjęcia. Tylko jeśli na stronie zamieścimy 5 zdjęć, które każde waży po 5-15 MB, to strona może nie tylko się długo ładować, ale i wolno chodzić.
Grzechem byłoby nie wspomnieć też o użytkownikach mobilnych, którzy często mają ograniczoną prędkość transmisji danych, a co więcej, urządzenia mobilne gorzej radzą sobie z przetwarzaniem dużych plików. Sprawi to, że samo przewijanie strony już będzie problemem – o ile strona się załaduje do końca.
Dlatego przed użyciem jakiegokolwiek obrazka z sieci warto sobie zadać pytania:
- Czy jego rozmiary są odpowiednie do osadzenia na stronę?
- Czy jego waga nie jest zbyt duża?
PORADA: Czasem zmniejszenie obrazka może tylko nieznacznie zmniejszyć jego wagę, lub po prostu potrzebujemy dużego obrazka. W takich przypadkach ważna jest jego kompresja. Jednym z serwisów, który umożliwia darmową kompresję obrazków, jest TinyPNG, który potrafi zmniejszyć wagę obrazków nawet o 80% bez utraty ich jakości!
HTML5 srcset
Skoro już wiemy, CO mamy zrobić, to warto jeszcze odpowiedzieć na pytanie, JAK to wdrożyć. Kiedy wrócisz do modułu o responsive, znajdziesz fragment o działaniu srcset z HTML5. Tym razem rozwiniemy nieco temat, ponieważ jest prosta metoda, aby przekazać różnym rodzajom urządzeń różne obrazki. Innymi słowy, możemy przygotować sobie zestaw obrazków dla różnych wielkości ekranu i serwować je w zależności od dostępnego viewportu.
<img
src="/images/foo.png"
alt="bar"
srcset="/images/foo-medium.png 1024w,
/images/foo-large.png 2048w,
/images/foo.png 800w"
/>
Jest jeszcze jedna na to metoda, polegająca na użyciu tagu picture, któremu przekazujemy źródła plików wraz z regułami. W tym przypadku użyte zostanie pierwsze źródło, którego reguły będą pasować.
<picture>
<source media="(min-width: 1024px)" srcset="foo-large.jpg 1024w, foo-medium.jpg 640w, foo-small.jpg 320w" sizes="50vw" />
<source srcset="foo@2x.jpg 2x, foo.jpg 1x" />
<img src="foo.jpg" alt="Bar" />
</picture>
Obrazki SVG
Kolejną metodą wyświetlania obrazków, szczególnie tych rysowanych czy też ikon są pliki SVG, czyli pliki wektorowe. Są one opisane matematycznie, dzięki czemu ich waga wynosi tyle, ile wynosi opisanie ich obiektów. Jakie są zalety plików wektorowych? Nieważne, czy będziemy chcieli wyświetlić 30x30px czy 4000x4000px, obrazek będzie zawsze idealnie równy – nie ma szans na pikselozę.
Osadzać SVG można na dwa sposoby. Możemy klasycznie podać źródło obrazka w atrybucie src:
<img src="file.svg">
lub po prostu wkleić zawartość pliku SVG do kodu HTML:
<div>
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
</svg>
</div>
Manipulacja SVG przez CSS
Jeśli osadzimy SVG jako HTML, to zyskujemy możliwość manipulowania nim za pomocą CSS.
Powyższy przykład generuje nam żółte kółko o rozmiarze 100x100px z zieloną obramówką. Jeśli chcielibyśmy zmienić jego kolor na czerwony, to musimy odwołać się do niego tak samo, jak do elementów HTML, tylko z użyciem właściwości zgodnych z SVG. W tym przykładzie zamiast background-color użyjemy fill:
svg circle {
fill: red;
}
Czcionki z ikonami
Ostatnią rzeczą dotyczącą SVG i obrazków są czcionki, które zamiast znaków mają ikony. Przykładem takiej czcionki jest Font Awesome. Zalet używania takich zestawów czcionek jest wiele:
- łatwe użycie,
- mała waga,
- mniejsza liczba rzeczy do pobrania,
- prosta manipulacja.
Używając takich czcionek, możemy szybko zmieniać wielkość czy kolor ikonek. O czym warto pamiętać to to, że takie ikonki muszą być jednokolorowe.
Jeśli chcesz stworzyć własny zestaw ikon na podstawie swoich plików SVG lub skorzystać z już stworzonych ikon, możesz taką czcionkę sobie wygenerować przy pomocy Fontello.
13.6. Wrapper formularza
Zacznijmy od podstaw, czyli budowy wrappera <form></form>, którego atrybuty będą wpływały na działanie naszego formularza.
Budowa formularza
Każdy formularz składa się z dwóch głównych elementów – wrappera i kontrolek.
Wrapper formularza jest tagiem <form>, w którego wnętrzu znajdują się pola formularza. Jego użycie pozwala nam oddzielić dane pomiędzy innymi formularzami znajdującymi się na stronie. W ten sposób zabezpieczamy sytuację, gdy mamy tak samo brzmiące pola w kilku formularzach, ale wysłać do serwera chcemy tylko jeden z nich.
Kontrolki formularza to wszelkiego rodzaju pola i przyciski, dzięki którym jesteśmy w stanie wprowadzić dane do formularza. W następnym submodule zostaną zaprezentowane najważniejsze z nich. Warto też zaznaczyć, że kontrolki mogą pełnić również funkcję zatwierdzającą (przycisk do wysłania).
Wewnątrz formularza warto też umieszczać inne elementy, np. divy, które pozwolą nam na grupowanie pól z opisami i ułatwią późniejsze stylowanie.
Poniżej znajduje się przykład formularza kontaktowego.
<form action="" method="POST">
<div class="field">
<label>Imię i nazwisko</label>
<input type="text" name="user-name">
</div>
<div class="field">
<label>Wiadomość</label>
<textarea name="message"></textarea>
</div>
<button type="submit">Wyślij</button>
</form>
Jak zauważysz w powyższym przykładzie, formularz rozpoczyna się od wrappera zawierającego dwa atrybuty: action i method.
Pierwszy z nich mówi, GDZIE mają zostać przekazane dane z formularza. Może to być adres URL skryptu, który przetworzy zapytanie. Pozostawiając je puste, zakładamy, że dane mają zostać przekazane pod adres, na którym aktualnie się znajdujemy.
Drugi parametr – method – określa, W JAKI SPOSÓB dane zostaną przekazane. Dysponujemy dwiema metodami, GET i POST, które za chwilę wyjaśnimy.
Kolejną rzeczą, na którą należy zwrócić uwagę, jest to, że każde pole zawiera atrybut name, dzięki, któremu będzie można zidentyfikować, z jakiego pola zostały przekazane dane.
I ostatnim elementem jest przycisk, za pomocą którego wyślemy dane z formularza.
Działanie formularzy
Na razie zajmujemy się jedynie wyglądem formularza. Jego pełne działanie będzie wymagało stworzenia backendu – w najprostszej formie byłby to np. skrypt na serwerze, który będzie potrafił odebrać dane wysłane za pomocą formularza i wysłać maila. Nie jest to jednak elementem tego projektu, i dlatego poniżej tylko pokrótce opisujemy jak argumenty tagu <form> wpływają na wysyłkę formularza.
Formularz kontaktowy na GitHub Pages
Nasz projekt będziemy publikować na GitHub Pages, który nie pozwala na zamieszczanie skryptów działających po stronie serwera. Jeśli jednak, w innych projektach w swoim portfolio, zechcesz umieścić działający formularz kontaktowy, możesz skorzystać z Formspree – darmowej platformy, która pozwala na wysyłanie maili na podstawie wypełnionego formularza.
Samo działanie większości formularzy wygląda następująco: użytkownik wypełnia formularz danymi, a następnie zatwierdza go, klikając przycisk typu submit. W przypadku, jeśli ustawimy polom atrybuty związane z walidacją (która domyślnie w HTML5 jest włączona), zostanie uruchomiony proces sprawdzania poprawności wypełnionych pól. Gdy formularz pomyślnie przejdzie walidację, zostanie wywołana akcja przekazania danych, z wykorzystaniem zadeklarowanej metody.
I tutaj warto się zatrzymać, aby zrozumieć, jakie mamy metody wysyłania formularzy.
GET
Metoda GET polega na przekazaniu danych poprzez adres URL. Formularz przejdzie do adresu zadeklarowanego w polu action, doklejając do niego pola formularza razem z zawartością. Np. https://kodilla.com/formularz?user-name=Michal&message=Hej%20przyjaciele
POST
Metoda POST odwoła się bezpośrednio do adresu z action, z tym że dane przekaże w niewidocznym dla użytkownika nagłówku zapytania. Ta metoda jest z reguły bezpieczniejsza.
Więcej na temat przetwarzania zapytań, w tym tych od formularzy, dowiesz się w następnych modułach.
13.7. Typy kontrolek
Czas poznać elementy, którymi dysponujemy przy tworzeniu formularzy. W tym submodule omówimy tylko te najczęściej wykorzystywane. Pełną listę elementów możesz znaleźć w dokumentacji MDN.
Input – pole tekstowe
Pole, które zawiera każdy formularz. Służy ono do wprowadzania zazwyczaj krótkiej, jednolinijkowej treści.
<input type="text" name="user-name">
Zauważ, że jako typ podaliśmy text, co oznacza, że pole jest tekstowym. Jednak nic nie szkodzi na przeszkodzie, żeby zrobić z niego pola typu:
email – gdzie już dodamy od razu regułę walidacji, wymagającą podania prawidłowego adresu email, gdy pole posiada też atrybut required.
password – pole z ukrywaną zawartością. Wszystko, co wprowadzimy, będzie ukryte za gwiazdkami/kropkami.
number – w którym będziemy mogli wprowadzać wartości liczbowe. Charakteryzują go również strzałki po prawej stronie, służące do zwiększania lub zmniejszania wartości. Dodając atrybuty min i max możemy określić zakres wprowadzanej liczby, a stosując atrybut step, zdefiniujemy wielkość pojedynczego przeskoku, np. jeśli chcemy za pomocą przycisków zmieniać wartość w polu o 4.
range – stworzy nam kontrolkę suwaka, która działa podobnie jak pole number. Aby określić zakres wartości oraz skok, również musimy skorzystać z atrybutów min, max i step.
To tylko kilka przykładów – pełną listę typów inputów znajdziesz w dokumentacji MDN.
UWAGA! W przypadku tych pól możemy ustawić atrybut value, który ustawi domyślną wartość dla tego pola.
Textarea – tekst wielolinijkowy
Pole, które umożliwia wprowadzenie długiego tekstu, np. treści wiadomości. Nie zawiera ono żadnych typów – jest tylko tekstowe.
<textarea name="message"></textarea>
UWAGA! W przypadku tego pola domyślną zawartość ustawiamy poprzez wprowadzenie jej między klamry znacznika.
<textarea name="message">Moja wiadomość</textarea>
Checkbox
Klasyczne pole wielokrotnego wyboru. Możemy je zaznaczyć i odznaczyć. Może istnieć samodzielnie lub jako grupa (w tym przypadku muszą mieć ten sam name, ale różne value).
<input type="checkbox" name="zgoda">
Radio button
Pole jednokrotnego wyboru występujące w grupie o tym samym name. Co istotne, raz kliknięta opcja nie może być odkliknięta, a jedynie zmieniona na inną. Różnicowanie polega na użyciu różnego value.
<input type="radio" name="kolor" value="red">
<input type="radio" name="kolor" value="green">
<input type="radio" name="kolor" value="blue">
Select
Rozwijana lista, umożliwiająca wybranie domyślnie jednego elementu. Tworzymy kontrolkę <select></select>, w którą wpisujemy kolejne opcje wyboru <option></option>. Domyślnie zaznaczona jest pierwsza opcja.
<select name="kolor">
<option value="red">Czerwony</option>
<option value="green">Zielony</option>
<option value="blue">Niebieski</option>
</select>
Istnieje dodatkowo wrapper, który umożliwia nam wizualne pogrupowanie opcji. Wystarczy owinąć je elementem <optgroup></optgroup>, gdzie w atrybucie label podamy opis grupy.
<select name="car">
<optgroup label="Szwedzkie">
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
</optgroup>
<optgroup label="Niemieckie">
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</optgroup>
</select>
Pole wyboru pliku
Umożliwia nam wybranie pliku, który ma zostać wysłany do serwera.
UWAGA! Przekazywanie pliku możliwe jest wyłączenie za pomocą metody POST oraz wymaga dodania do formularza atrybutu enctype="multipart/form-data".
<form method="post" enctype="multipart/form-data">
<div>
<label>Wybierz plik do wgrania</label>
<input type="file" name="file">
</div>
<div>
<button>Wyślij</button>
</div>
</form>
Dodatkowo za pomocą atrybutu accept możemy podać akceptowane rozszerzenia pliku lub jego typ (MIME type).
<input type="file" accept=".png, .jpeg">
<input type="file" accept="image/png, image/jpeg">
Button
Aby zatwierdzić wysłanie formularza, potrzebny nam jest przycisk z etykietą. Domyślnie przycisk w formularzu "submituje" go, jednak przycisków możemy używać dowolnie. Najważniejsze jest określenie jego typu (type).
<button type="submit">Przycisk</button>
Jednak w identyczny sposób możemy stworzyć przycisk, wykorzystując do tego <input> z typem submit. Etykietą przycisku będzie jego value.
<input type="submit" value="Przycisk">
Różnica pomiędzy buttonem a inputem typu submit
Jeśli potrzebujemy więcej elastyczności i możliwości kontroli zawartości przycisku, np. chcemy dodać ikonkę do niego, to lepiej skorzystać z opcji <button></button>.
Z drugiej jednak strony, jeśli chcemy mieć możliwość przekazania dodatkowej wartości po kliknięciu przycisku, to możemy skorzystać z opcji z <input>. Przykładem sytuacji może być formularz, w którym określamy dwie różne akcje – zapis lub usunięcie obiektu. Możemy do tego formularza dodać dwa przyciski:
<input type="submit" name="akcja" value="zapisz">
<input type="submit" name="akcja" value="usun">
W zależności od tego, w który przycisk klikniemy, taką wartość przyjmie pole o nazwie akcja.
Ukryte pole
Jeśli chcemy w formularzu przekazać ukryte dane, które mają się nie pojawiać użytkownikowi, wtedy możemy skorzystać z pola typu hidden.
<input type="hidden" name="nazwa_pola" value="1">
Label
Jest to etykieta dla pola, która je opisuje. Często służy jako dodatkowy element interfejsu do zaznaczenia pola.
<label>Wybierz plik do wgrania</label>
Umieszczając w jego wnętrzu jakieś pole, np. checkbox czy radio, sprawimy, że po jego kliknięciu pole nam się zaznaczy, a przypadku pól tekstowych stanie się aktywne (:focus).
<label>
<input type="checkbox" name="zgoda">
Wyrażam zgodę na przetwarzanie danych osobowych...
</label>
Jeśli jednak chcemy mieć wydzielone pole od etykiety, wtedy możemy je powiązać z pomocą atrybutu for i podać id pola, z którym ma być powiązane.
<input type="checkbox" name="zgoda" id="poleZgody">
<label for="poleZgody">Wyrażam zgodę na przetwarzanie danych...</label>
13.8. Właściwości kontrolek
Poza wspomnianymi atrybutami warto również zapoznać się z listą najpopularniejszych atrybutów stosowanych przy różnych typach pól.
| Atrybut | Obsługiwane pola | Działanie |
|---|---|---|
name |
wszystkie pola | Każde pole musi zawierać nazwę, abyśmy mogli identyfikować jego zawartość. Jego brak skutkować będzie brakiem wysłania jego wartości. |
value |
wszystkie pola z wyjątkiem textarea | Ustawia wartość pola |
selected |
option | Określa, czy opcja jest wybrana |
checked |
checkbox, radio | Określa, czy checkbox/radio jest zaznaczony |
maxlenght |
textarea i pola tekstowe | Ogranicza możliwą liczbę wprowadzonych znaków |
autocomplete |
textarea i pola tekstowe | Określa, czy można uzupełnić pole wcześniej wprowadzonymi wartościami, jeżeli takowe są zapisane w przeglądarce |
multiple |
pole wyboru pliku, select | Określa, czy możliwe jest wybranie kilku opcji/plików |
placeholder |
pola tekstowe, textarea | Ustawia tekst pokazujący się, jeśli pole jest niewypełnione |
required |
wszystkie pola | Określa, czy wymagane jest wypełnienie pola przed wysłaniem formularza |
readonly |
wszystkie pola | Blokuje możliwość edycji pola |
disabled |
wszystkie pola | Blokuje możliwość edycji pola oraz jego wysyłki w formularzu |
Różnica pomiędzy readonly a disabled
Jeśli zależy Ci na pokazaniu pola użytkownikowi, ale nie chcesz dawać mu możliwości jego edycji, możesz wybrać jedną z dwóch opcji: readonly lub disabled. Jedno i drugie ma podobne działanie. Różnica polega na tym, że to, co ma readonly, zostanie wysłane jak normalne pole, a to, co jest disabled, zostanie pominięte.
13.9. Stylowanie kontrolek
Bardzo ważne jest odpowiednie dostosowanie wyglądu kontrolek do designu lub ogólnego stylu strony. Poniższe porady pozwolą Ci uniknąć wielu problemów, które napotykają początkujący Web Developerzy.
Inputy tekstowe i textarea
Najprostsze do ostylowania są z pewnością inputy tekstowe i textarea. Można nimi zarządzać jak każdym blokiem, ustawiać im kolor tła, kolor czcionki, zmieniać padding itp.
input[type="text"] {
padding: 5px 10px;
font-size: 14px;
background-color: #fafafa;
border: none;
color: #000000;
outline: none;
}
Warto zwracać uwagę, by przekazywać w CSS typ pola, które stylujemy, żeby uniknąć konfliktów z inputami różnych typów np. tekstu z checkboksem lub przyciskiem.
input[type="text"],
input[type="email"] {
...
}
Pewnie zastanawiasz się też, jak to się dzieje, że kiedy jesteś w polu i wprowadzasz tekst, to widać dookoła niebieską obwódkę. Jest to outline, który możesz wyłączyć, używając w CSS reguły outline: none. Pamiętaj jednak, że warto w jakiś sposób wyróżnić aktywne pole – możesz do tego wykorzystać style dla stanu kontrolki, które opisujemy poniżej.
Dodatkowo w przypadku textarea domyślnie mamy ustawioną możliwość zmiany wielkości pola. To ten mały trójkącik w prawym dolnym rogu pola, który wystarczy przeciągnąć, aby zmiana się dokonała. Jak zablokować tę możliwość? Dzięki regule resize: none.
Checkboksy i radio buttony
Stylowanie inputów tekstowych i textarea było rzeczą dość prostą, jednak stylowanie checkboksów i radio buttonów jest wyzwaniem. A to wszystko dlatego, że te kontrolki nie reagują na stylowanie z wyjątkiem pozycjonowania i widoczności.
Przychodzi zatem pytanie, w jaki sposób zrobić ładnie wyglądającą kontrolkę. Tutaj skorzystamy z wiedzy o stosowaniu labelów oraz stanów (:checked) pól typu checkbox czy radio.
Zwróć uwagę w powyższym przykładzie na te aspekty:
- nasz
input[type="checkbox"]ukrywamy nadając muopacity:0, - pozycja absolutna sprawi, że input nałoży się na
.icon, - wymiary checkboksa muszą być takie same jak wymiary
.icon, - wykorzystujemy
box-sizing: border-box, aby szerokość obramowania wliczała się wwidthiheight.
Dzięki temu podejściu checkbox będzie przezroczysty i będzie przykrywał .icon. Jest to potrzebne dla systemów, w których kliknięcie w <label> nie zaznacza checkboksa.
W ten sposób stworzyliśmy działającą własną kontrolkę dla checkboksa. Analogicznie możemy zrobić to z radio buttonem. Pamiętaj tylko, że radio button najczęściej przyjmuje kształt koła, a checkbox kwadratu.
Selecty
Podobnie jak checkboksy i radio buttony, selecty również musimy stylować w nieco bardziej skomplikowany sposób. Dzieje się tak przede wszystkim dlatego, że przeglądarki narzucają im swoje style (każda przeglądarka używa własnych stylów). Style te noszą nazwę webappearance i przed rozpoczęciem stylowania należy je wyłączyć.
select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
W ten sposób nie tylko usunęliśmy strzałki, które narzucała nam przeglądarka, ale również część stylów. Dlatego powinniśmy zacząć właśnie od ich uzupełnienia:
select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: #ffffff;
border: 1px solid #000000;
padding: 5px 30px 5px 10px;
font-size: 14px;
display: block;
border-radius: 0;
}
Specjalnie zostawiliśmy po prawej stronie dodatkowy padding 20px, aby zmieścić tam strzałkę, którą dodamy za pomocą obrazku tła. Finalny efekt będzie następujący:
W ten sposób zrobiliśmy własne style dla selecta.
Stany kontrolek
Stylując kontrolki warto też pamiętać o ich wyglądzie dla różnych stanów. Im więcej stanów mamy obsłużonych dla każdej kontrolki, tym mniej może nas zaskoczyć przy implementacji i użytkowaniu formularza.
Stany, które warto obsłużyć:
:disabled i :readonly, kiedy pole jest zablokowane.
:focus, kiedy pole jest aktywne i wprowadzamy do niego treści.
:hover – nie jest wymagany, ale kiedy najedziemy na pole, za pomocą stanu :hover możemy pokazać, że jest ono aktywne.
:checked, kiedy pole jest zaznaczone.
:valid lub :invalid, kiedy walidacja poprawności pola przeszła lub nie przeszła pomyślnie.
13.10. Walidacja formularzy
Skoro jesteśmy przy temacie formularzy, grzechem będzie nie wspomnieć o walidacji. Jeśli już chcemy przekazać do serwera dane, to warto przed wykonaniem jakiegokolwiek zapytania sprawdzić, czy przesłane dane są prawidłowe. Ten proces nazywamy właśnie walidacją formularza.
Walidację możemy przeprowadzić w dwojaki sposób – dzięki wbudowanym funkcjom HTML5 oraz wsparcia przeglądarki lub poprzez napisanie własnego walidatora.
Walidacja HTML5
Każdy formularz i pole przyjmuje w każdym momencie jeden z dwóch stanów: :valid lub :invalid. W zależności od tego, w jaki sposób są wypełnione pola i czy ich zawartość jest zgodna z wymaganiami walidacji, taki stan otrzyma cały formularz.
W jaki sposób dodać wspomniane reguły? Za pomocą atrybutów w polach:
required – pojawił się już wcześniej wielokrotnie i wymaga, żeby pole nie było puste.
min i max – określają minimalną i maksymalną wartość pola liczbowego.
minlength i maxlength – określają minimalną i maksymalną długość wartości pola tekstowego.
pattern – określa reguły za pomocą wyrażeń regularnych (regex). Więcej na ten temat znajdziesz w dokumentacji
Po ustawieniu takich atrybutów, po próbie zasubmitowania formularza w błędnych polach zobaczymy komunikaty walidacji.
Własne reguły walidacji w JS
Jeśli jednak chcielibyśmy napisać własną walidację i sterować nią za pomocą JavaScriptu, należy zacząć od wyłączenia HTML-owej walidacji dodając do <form> atrybut novalidate.
<form novalidate>
<label for="mail">
<span>Podaj swój adres e-mail:</span>
<input type="email" id="mail" name="email_address">
<span class="error"></span>
</label>
<button>Zapisz</button>
</form>
Następnie w JavaScripcie powinniśmy wykryć próbę submitowania.
document.querySelector('form').addEventListener('submit', function(event) {
...
})
A następnie określić, czy formularz powinien być zwalidowany, czy nie.
document.querySelector('form').addEventListener('submit', function(event) {
var isFormValidate = true;
// Tutaj napiszemy reguły walidacji
return !isFormValidate ? event.preventDefault() : true;
})
Spróbujmy teraz napisać walidację pola o nazwie email_address:
document.querySelector('form').addEventListener('submit', function(event) {
var isFormValidate = true;
var emailAddressInput = event.target.querySelector('input[name="email_address"]')
if(emailAddressInput.value.indexOf('@') < 0) {
isFormValidate = false;
emailAddressInput.parentElement.querySelector('.error').innerHTML = 'Błędny adres e-mail';
}
return !isFormValidate ? event.preventDefault() : true;
})
W ten sposób napisaliśmy prostą walidację formularza, którą możemy rozbudowywać.